

import Linear from './base_functions/Linear';
import Rec from './base_functions/Rec';
import Quadratic from './base_functions/Quadratic';
import Power from './base_functions/Power';
import Exp from './base_functions/Exp';
import Log from './base_functions/Log';
import Sin from './base_functions/Sin';
import Cos from './base_functions/Cos';
import Tan from './base_functions/Tan';
import Cmd from './Cmd';
import SinXDX from './base_functions/SinXDX';
import E from './base_functions/E'
import EPX from './base_functions/EPX';
import XMSinX from './base_functions/XMSinX';

export default class Main {
	constructor(){
		this.name="核心模块";
		this.id= "main";
		this.version="1.3"; //模块版本
		this.program= "0.10.0"; //本模块是为该版本的主程序设计
		this.canvas=null;
		this.logOutPut = document.getElementById("console") //日志输出
		this.initBtn = document.getElementById("init"); //初始化按钮
		/**
		 * @type {CanvasRenderingContext2D}
		 */
		this.ctx = null; //CanvasRenderingContext2D
		this.coordinate={
				axisWidth:2, //坐标轴宽度
				units: 100, //单位长度（以像素计算）
				x: 0, //原点水平偏移，0为正中间，负为向左，正为向右
				y: 0, //原点垂直偏移，0为正中间，负为向下，正为向上
			};
		// 函数列表开始
		this.functions = {};
		this.functions['linear'] = Linear;
		this.functions['rec'] = Rec;
		this.functions['quadratic'] = Quadratic;
		this.functions['power'] = Power;
		this.functions['exp'] = Exp;
		this.functions['log'] = Log;
		this.functions['sin'] = Sin;
		this.functions['cos'] = Cos;
		this.functions['tan'] =Tan;
		this.functions['sinXDX'] =SinXDX;
		this.functions['e'] =E;
		this.functions['epx'] =EPX
		this.functions['xmsinx'] = XMSinX;
		
		// 函数列表结束
		this.functionObjectList = []// 用于react的props;
		this.cmd= new Cmd(this);
		this._logList=[];
	}

	// x取值点列表
	get xPointArray() {
		const pointCount = (this.maxXInt-this.minXInt)*this.coordinate.units;
		return new Array(pointCount).fill(0).map((v,i)=>i/this.coordinate.units+this.minXInt)
	}

	/**
	 * 画函数
	 * @param {(n:number)=>number} f 函数
	 */
	draw (f) {
		console.warn('0.11版本特别函数绘制接口被调用！')
		this.beginPath();
		let outSide = false; // 超限的就不要画了
		this.xPointArray.forEach((x,i)=> {
			let y = f(x)
			let xy=this.coor(x,y)
			// console.log(y)
			// 100是只暂定，以后写个算法根据 绘制范围与精度动态调整
			if (i && Math.abs(y) <20) {
				this.lineTo(xy.x, xy.y)
			} else {
				console.log('不要画', y);
				this.ctx.moveTo(xy.x, xy.y)
			}
		})
		this.ctx.strokeStyle="#438EDB";
		this.stroke()
	}

	get length() {
		return Object.keys(this.functions).length;
	}

	get list() {
		return Object.keys(this.functions);
	}
	get functionList(){
		return this.functionObjectList;
	}
	get logList(){
		return [].concat(this._logList);
	}

	get width(){
		return this.canvas.width;
	}
	get height(){
		return this.canvas.height;
	}

	get units(){
		return this.coordinate.units;
	}
	//控制台输出
	consoleMsg (msg) {
		this._logList.push(msg);
	}
	//封装一层的log
	log(model,msg){
		this.consoleMsg(model+"信息："+msg);
	}
	init(){
		console.log('main.init')
		for (let i = 0; i < this.length; i++) {
			let name = this.list[i];
			let Fun = this.functions[name];
			this.functionObjectList.push(new Fun(this));
		}
		this.canvas = document.getElementById("xy");
		this.ctx = this.canvas.getContext("2d");
		this.xys();
		this.cmd.init();
	}
	//获取x在坐标系统中的最小/大值
	getXmin(){
		return -this.canvas.width / this.coordinate.units / 2 + this.coordinate.x
	}
	getXmax(){
		return this.canvas.width / this.coordinate.units / 2 + this.coordinate.x
	}
	//获取x在坐标系统中的最小/最大整值
	get maxXInt(){
		return Math.round(this.getXmax());
	}
	get minXInt(){
		return Math.round(this.getXmin());
	}
	//获取y在坐标系统中最大/最小值
	get maxY(){
		return this.height/this.units/2;
	}
	get minY(){
		return -this.height / this.units / 2;
	}
	//获取y在坐标系统中最大/最小整值
	get maxYInt(){
		return Math.round(this.maxY);
	}
	get minYInt(){
		return Math.round(this.minY);
	}
	getMinUnit(){
		return 1 / this.coordinate.units
	}
	//绘制坐标系统
	xys(){
		let xy = this.ctx||this.canvas.getContext("2d");
		xy.clearRect(0, 0, this.canvas.width, this.canvas.height);
		xy.beginPath();
		//画网格
		this.lineStyle ='#999999';
		let c;
		for(let i=this.minYInt;i<this.maxYInt;i++){
			c = this.coor(this.getXmin(), i)
			this.moveTo(c.x,c.y);
			c = this.coor(this.getXmax(), i)
			this.lineTo(c.x, c.y);
			this.stroke();
		}
		for (let i=this.minXInt;i<this.maxXInt;i++){
			c = this.coor(i, this.minY)
			this.moveTo(c.x, c.y);
			c = this.coor(i, this.maxY)
			this.lineTo(c.x, c.y);
			this.stroke();
		}
		this.stroke();
		//画主轴
		this.beginPath()
		xy.lineWidth = this.coordinate.axisWidth;
		this.lineStyle ="#2D2D2D";
		xy.moveTo(0, this.canvas.height / 2 + this.coordinate.y * this.coordinate.units);
		xy.lineTo(this.canvas.width, this.canvas.height / 2 + this.coordinate.y * this.coordinate.units);
		xy.stroke();
		xy.moveTo(this.canvas.width / 2 + this.coordinate.x * this.coordinate.units, 0);
		xy.lineTo(this.canvas.width / 2 + this.coordinate.x * this.coordinate.units, this.canvas.height);
		xy.stroke();
		this.lineStyle ='#FF3F3F'
		this.log(this.name , "画布初始化完成。");
	}
	//将数学坐标转换为屏幕坐标
	coor(x,y){
		console.log(x,y)
		if(x>0){
			x = x * this.coordinate.units + this.canvas.width / 2;
		}else if(x==0){
			x = this.canvas.width / 2;
		}else if(x<0){
			x = this.canvas.width / 2 - this.coordinate.units * Math.abs(x);
		}
		if(y>0){
			y = this.canvas.height / 2 - this.coordinate.units * y;
		}else if(y==0){
			y = this.canvas.height / 2;
		}else if(y<0){
			y = Math.abs(y * this.coordinate.units) + this.canvas.height / 2;
		}
		return {
			x,y
		};
	}
	//下面的方面都是注入CanvasRenderingContext2D的方法

	set lineStyle(style){
		this.ctx.strokeStyle=style;
	}
	/**
	 * 通过清空子路径列表开始一个新路径的方法。 当你想创建一个新的路径时，调用此方法。
	 */
	beginPath(){
		this.ctx.beginPath();
	}
	/**
	 * 使用直线连接子路径的终点到x，y坐标的方法（并不会真正地绘制）。
	 * @param {number} x 如参数名
	 * @param {number} y 如参数名
	 */
	lineTo(x,y){
		this.ctx.lineTo(x,y);
	}
	/**
	 * 将一个新的子路径的起始点移动到(x，y)坐标的方法。
	 * @param {number} x 如形参名
	 * @param {number} y 如形参名
	 */
	moveTo(x,y){
		this.ctx.moveTo(x,y)
	}
	/**
	 * 使用非零环绕规则，根据当前的画线样式，绘制当前或已经存在的路径的方法。
	 */
	stroke(){
		this.ctx.stroke();
	}
}



